package org.oslims.client.presenter;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.oslims.client.AsyncCall;
import org.oslims.client.MainEventBus;
import org.oslims.client.service.SampleServiceAsync;
import org.oslims.client.service.SampleTypeServiceAsync;
import org.oslims.client.uicomponents.SelectionColumn;
import org.oslims.client.uicomponents.SortableHeader;
import org.oslims.client.uicomponents.StyleableFlexTable;
import org.oslims.client.uicomponents.TagCheckBox;
import org.oslims.client.uicomponents.editbox.EditSampleBox;
import org.oslims.client.uicomponents.editbox.SearchSamplesBox;
import org.oslims.client.view.impl.SampleEditorViewImpl;
import org.oslims.domain.Request;
import org.oslims.domain.Sample;
import org.oslims.domain.SampleType;

import com.google.gwt.cell.client.ButtonCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.validation.client.InvalidConstraint;
import com.google.gwt.validation.client.interfaces.IValidator;
import com.google.gwt.view.client.ListView;
import com.google.gwt.view.client.Range;
import com.google.gwt.view.client.ListView.Delegate;
import com.mvp4g.client.annotation.InjectService;
import com.mvp4g.client.annotation.Presenter;
import com.mvp4g.client.presenter.BasePresenter;

@Presenter(view = SampleEditorViewImpl.class)
public class SamplePresenter extends BasePresenter<SampleEditorViewImpl, MainEventBus> {

	private IValidator<Sample> sampleValidator;
	private List<SampleType> sampleTypeList;

	private Request currentRequest;

	private SampleServiceAsync sampleService;
	private SampleTypeServiceAsync sampleTypeService;

	private String currentSortProperty = "code";
	private Boolean currentSortDesc = false;

	public SamplePresenter() {
		sampleValidator = GWT.create(Sample.class);
	}

	public void onInizializeSampleManagement(TabPanel tp) {
		tp.add(view.getViewWidget(), "Samples");
	}

	public void onRefreshSampleView(Request currentRequest) {
		this.currentRequest = currentRequest;
		refreshSampleTypes();
		refreshData();
	}

	public void bind() {

		bindUI();

		addColumns();

		setDelegate();

	}

	private void setDelegate() {
		view.getTable().setDelegate(new Delegate<Sample>() {

			@Override
			public void onRangeChanged(ListView<Sample> listView) {

				final Range r = listView.getRange();

				sampleService.getSamplesByRequest(currentRequest, r.getStart(), r.getLength(), currentSortProperty, currentSortDesc, new AsyncCall<List<Sample>>() {

					@Override
					protected void onFailureE(Throwable caught) {
						eventBus.error(caught.getMessage());
					}

					@Override
					protected void onSuccessE(List<Sample> result) {
						view.getTable().setData(r.getStart(), r.getLength(), result);
						view.getTable().redraw();
					}
				});
			}
		});
	}

	private void addColumns() {

		SelectionColumn<Sample> selColumn = new SelectionColumn<Sample>(view.getSelectionModel());
		view.getTable().addColumn(selColumn);

		final SortableHeader codeHeader = new SortableHeader("Code", "code");
		codeHeader.setSorted(true);

		codeHeader.setUpdater(new ValueUpdater<String>() {

			@Override
			public void update(String value) {
				codeHeader.toggleReverseSort();
				currentSortProperty = codeHeader.getPropertyName();
				currentSortDesc = codeHeader.getReverseSort();
				view.getTable().refresh();
				view.getTable().refreshHeaders();
			}
		});

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getCode();
			}
		}, codeHeader);

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getSampleType().toString();
			}
		}, "Sample Type");

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getOrigin();
			}
		}, "Origin");

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getDescription();
			}
		}, "Description");

		view.getTable().addColumn(new TextColumn<Sample>() {

			@SuppressWarnings("deprecation")
			@Override
			public String getValue(Sample object) {
				return object.getSamplingTime().toLocaleString();
			}
		}, "Sampling Date/Time");

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getStoragePlace();
			}
		}, "Storage Place");

		view.getTable().addColumn(new TextColumn<Sample>() {

			@Override
			public String getValue(Sample object) {
				return object.getState();
			}
		}, "State");

		Column<Sample, String> editColumn = new Column<Sample, String>(new ButtonCell()) {

			@Override
			public String getValue(Sample object) {
				return "Edit";
			}
		};

		editColumn.setFieldUpdater(new FieldUpdater<Sample, String>() {

			@Override
			public void update(int index, Sample object, String value) {

				int selectedIndex = -1;
				List<String> sampleTypes = new ArrayList<String>();

				for (SampleType st : sampleTypeList) {
					sampleTypes.add(st.toString());
					if (st.toString().equals(object.getSampleType().toString())) {
						selectedIndex = sampleTypes.size() - 1;
					}
				}

				view.getEditSampleBox().getSampleType().setSelectionItems(sampleTypes);
				view.getEditSampleBox().getSampleType().getInputBox().setSelectedIndex(selectedIndex);

				view.getEditSampleBox().showEdit(object);
			}
		});

		view.getTable().addColumn(editColumn);
	}

	private void refreshData() {
		sampleService.getSampleCount(currentRequest, new AsyncCall<Integer>() {

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}

			@Override
			protected void onSuccessE(Integer result) {
				view.getTable().setDataSize(result, true);

				if (result < view.getDefaultPageSize()) {
					view.getPager().setVisible(false);
					view.getTable().setPageSize(result);
				}
				view.getTable().refresh();
			}
		});
	}

	private void bindUI() {
		view.getAddSampleButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				List<String> sampleTypes = new ArrayList<String>();

				for (SampleType st : sampleTypeList) {
					sampleTypes.add(st.toString());
				}

				view.getEditSampleBox().getSampleType().setSelectionItems(sampleTypes);
				view.getEditSampleBox().showNew();
			}
		});

		view.getCopySampleButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				copySelectedSample();
			}
		});

		view.getMarkSamplesAsRecievedButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {

				if (view.getSelectionModel().getSelectedSet().size() == 0) {
					eventBus.error("No Sample selected!");
					return;
				}

				view.getEnterStoragePlaceBox().show();
			}
		});

		view.getEnterStoragePlaceBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				String storagePlace = view.getEnterStoragePlaceBox().getStoragePlaceField().getValue();

				if (storagePlace.length() > 0) {
					markSelectedStored(view.getEnterStoragePlaceBox().getStoragePlaceField().getValue());
					view.getEnterStoragePlaceBox().hide();
				} else {
					eventBus.error("Storage Place cannot be empty");
				}

			}
		});

		view.getImportSamplesButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				view.getSearchSamplesBox().show();
			}
		});

		view.getSearchSamplesBox().getSearchButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				searchSamples();
			}
		});

		view.getSearchSamplesBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				view.getSearchSamplesBox().hide();
			}
		});

		view.getSearchSamplesBox().getOkButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				importSamples();
			}
		});

		view.getEnterStoragePlaceBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent event) {
				view.getEnterStoragePlaceBox().hide();
			}

		});

		view.getEditSampleBox().getCancelButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				view.getEditSampleBox().hide();
			}
		});

		view.getEditSampleBox().getSaveButton().addClickHandler(new ClickHandler() {

			@Override
			public void onClick(ClickEvent arg0) {
				if (view.confirmEdit("Do you want to save your changes?"))
					saveSample();
			}
		});
	}

	protected void importSamples() {
		final StyleableFlexTable searchTable = view.getSearchSamplesBox().getSearchTable();

		List<Sample> importList = new ArrayList<Sample>();

		for (int i = 2; i < searchTable.getRowCount(); i++) {
			TagCheckBox cbx = (TagCheckBox) searchTable.getWidget(i, 0);

			if (cbx.getValue())
				importList.add((Sample) cbx.getTag());
		}

		if (importList.size() > 0) {
			sampleService.importSamplesForRequest(importList, currentRequest, new AsyncCall<Void>() {

				@Override
				protected void onSuccessE(Void result) {
					view.getSearchSamplesBox().hide();
					refreshData();
				}

				@Override
				protected void onFailureE(Throwable caught) {
					eventBus.error(caught.getMessage());
				}
			});
		}

	}

	protected void searchSamples() {

		SearchSamplesBox searchBox = view.getSearchSamplesBox();
		final StyleableFlexTable searchTable = view.getSearchSamplesBox().getSearchTable();

		String sampleCode = searchBox.getCodeBox().getValue();
		String description = searchBox.getDescriptionBox().getValue();
		String origin = searchBox.getOriginBox().getValue();
		String request = searchBox.getRequestBox().getValue();
		String sampleType = searchBox.getSampleTypeBox().getValue();
		String storagePlace = searchBox.getStoragePlaceBox().getValue();

		if (sampleCode == null)
			sampleCode = "";
		if (description == null)
			description = "";
		if (origin == null)
			origin = "";
		if (request == null)
			request = "";
		if (sampleType == null)
			sampleType = "";
		if (storagePlace == null)
			storagePlace = "";

		sampleService.searchSamples(sampleCode, description, origin, request, sampleType, storagePlace, new AsyncCall<List<Sample>>() {

			@Override
			protected void onSuccessE(List<Sample> result) {

				while (searchTable.getRowCount() > 2)
					searchTable.removeRow(2);

				for (int i = 0; i < result.size(); i++) {
					searchTable.setWidget(i + 2, 0, new TagCheckBox(result.get(i)));
					searchTable.setText(i + 2, 1, result.get(i).getCode());
					searchTable.setText(i + 2, 2, result.get(i).getDescription());
					searchTable.setText(i + 2, 3, result.get(i).getOrigin());
					String requestList = "";

					for (Request r : result.get(i).getRequests()) {
						requestList += r.getCode() + ", ";
					}
					searchTable.setText(i + 2, 4, requestList.substring(0, requestList.length() - 2));
					searchTable.setText(i + 2, 5, result.get(i).getSampleType().getName());
					searchTable.setText(i + 2, 6, result.get(i).getStoragePlace());
				}
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	protected void markSelectedStored(String storagePlace) {

		List<Sample> selectedSamples = new ArrayList<Sample>();

		for (Sample s : view.getSelectionModel().getSelectedSet()) {
			selectedSamples.add(s);
		}

		sampleService.markSamplesAsRecieved(selectedSamples, storagePlace, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {
				refreshData();
			}

			@Override
			public void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	protected void copySelectedSample() {

		int i = view.getSelectionModel().getSelectedSet().size();

		if (i > 1) {
			eventBus.error("Only one sample can be copied at a time");
			return;
		}

		if (i == 0) {
			eventBus.error("No Sample selected!");
			return;
		}

		Sample newSample = new Sample();
		Sample existingSample = null;
		for (Sample s : view.getSelectionModel().getSelectedSet()) {
			existingSample = s;
		}

		newSample.setDescription(existingSample.getDescription());
		newSample.setOrigin(existingSample.getOrigin());
		newSample.getRequests().add(currentRequest);
		newSample.setSampleType(existingSample.getSampleType());
		newSample.setSamplingTime(existingSample.getSamplingTime());
		newSample.setState(existingSample.getState());
		newSample.setStoragePlace(existingSample.getStoragePlace());

		sampleService.saveOrUpdateSample(newSample, new AsyncCall<Void>() {

			@Override
			protected void onSuccessE(Void result) {
				refreshData();
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	private void saveSample() {

		EditSampleBox editBox = view.getEditSampleBox();
		Sample tempSample = editBox.getSample();

		if (editBox.getSample() == null) {
			tempSample = new Sample();
			tempSample.setState("Not Recieved");
		}

		tempSample.setOrigin(editBox.getOrigin().getValue());
		tempSample.setDescription(editBox.getDescription().getHTML());
		tempSample.setSamplingTime(editBox.getSamplingTime().getValue());

		if (!tempSample.getRequests().contains(currentRequest)) {
			tempSample.getRequests().add(currentRequest);
		}

		String selectedSampleType = editBox.getSampleType().getInputBox().getItemText(editBox.getSampleType().getInputBox().getSelectedIndex());

		for (SampleType st : sampleTypeList) {
			if (st.toString().equals(selectedSampleType)) {
				tempSample.setSampleType(st);
				break;
			}
		}

		Set<InvalidConstraint<Sample>> sampleErrors = sampleValidator.validate(tempSample);

		if (sampleErrors.size() > 0) {
			for (InvalidConstraint<Sample> ic : sampleErrors) {
				editBox.showError(ic.getItemName(), ic.getMessage());
			}
			return;
		}

		sampleService.saveOrUpdateSample(tempSample, new AsyncCall<Void>() {

			@Override
			public void onSuccessE(Void result) {

				view.getEditSampleBox().hide();
				refreshData();
			}

			@Override
			public void onFailureE(Throwable error) {
				eventBus.error(error.getMessage());
			}
		});

	}

	private void refreshSampleTypes() {

		sampleTypeService.getAllSampleType(new AsyncCall<List<SampleType>>() {

			@Override
			protected void onSuccessE(List<SampleType> result) {
				sampleTypeList = result;
			}

			@Override
			protected void onFailureE(Throwable caught) {
				eventBus.error(caught.getMessage());
			}
		});

	}

	@InjectService
	public void setSampleService(SampleServiceAsync sampleService) {
		this.sampleService = sampleService;
	}

	@InjectService
	public void setSampleTypeService(SampleTypeServiceAsync sampleTypeService) {
		this.sampleTypeService = sampleTypeService;
	}
}
